#include "objectManagement.h"
#include "skeletonAlgo.h"
#include <math.h>

//! \brief Initialize the object's parameters
//!
//! \param mainMat The main object matrix
//! \param index This object's index
//!
Object::Object(OBJ_INDEX** mainMat, OBJ_INDEX index)
{
    m_mainMat = mainMat;
    m_count = 0;
    m_index = index;
    m_area.x = MAX_IMG_SIZE; m_area.y = MAX_IMG_SIZE; m_area.w = 0; m_area.h = 0;
    m_isobar.lign = 0; m_isobar.column = 0;
    m_skel = NULL;
}

//! \brief Add a point to the object
//!
//! \param li The point's lign
//! \param col The point's column
//! \param src The point's Lab color
//!
void Object::addPoint(Uint16 li, Uint16 col, PixelLab** src)
{
    m_mainMat[li][col] = m_index;
    m_area.x = std::min(m_area.x, col);
    m_area.y = std::min(m_area.y, li);
    m_area.w = std::max(m_area.w, col);
    m_area.h = std::max(m_area.h, li);
    m_mainColor = weightedAverage(m_mainColor, (double)m_count/(m_count+1), src[li][col], 1.0/(m_count+1));
    m_isobar.lign += li; m_isobar.column += col;
    m_count++;
}

//! \brief Calculates the final area, RGB color and isobaricenter
void Object::finalize()
{
    m_area.w = m_area.w - m_area.x +1; m_area.h = m_area.h - m_area.y +1;
    m_mainRGB = LabtoRGB(m_mainColor);
    m_isobar.lign = m_isobar.lign / m_count;
    m_isobar.column = m_isobar.column / m_count;
}

//! \brief Processes the object's skeleton
void Object::processSkeleton()
{
    if(m_count < SKEL_MAX_SIZE)
    {
        m_skel = new Skeleton(m_mask, m_area);
        m_skel->process();
    }
}

//! \brief Processes the object's mask
void Object::processMask()
{
    m_mask = createMatrix(getSize(), false);
    int li, col;
    for(li=0;li<m_area.h;li++)
    {
        for(col=0;col<m_area.w;col++)
        {
            m_mask[li][col] = (m_mainMat[li+m_area.y][col+m_area.x] == m_index);
        }
    }
}

//! \brief Destructs the object
Object::~Object()
{
    freeMatrix(m_mask, getSize());
    delete m_skel;
}

//! \brief Initializes the object manager
ObjectManager::ObjectManager(Taille imgSize)
{
    m_mat = createMatrix(imgSize, (OBJ_INDEX)NO_OBJECT);
    m_current = -1;
    m_size = imgSize;
}

//! \brief Applies a function to each object
//!
//! \param ptr A function to apply
void ObjectManager::iterate(void (Object::*ptr)(void))
{
    for(std::vector<Object*>::iterator it = m_objs.begin(); it!=m_objs.end(); it++)
    {
        ((*it)->*ptr)();
    }
}

//! \brief Expands the objects till no unassigned pixels are left
//!
//! \param src The base color matrix
void ObjectManager::processLeftover(PixelLab** src)
{
    int li, col;
    bool done = false;
    OBJ_INDEX** changes = createMatrix(m_size, (Uint16)NO_OBJECT);
    while (!done)
    {
        done=true;
        for (li=1;li<m_size.nbrlign-1;li++)
        {
            for (col=1;col<m_size.nbrcolumn-1;col++)
            {
                if(m_mat[li][col]==NO_OBJECT)
                {
                    //TODO : change replacement method to color proximity
                    if(m_mat[li-1][col] != NO_OBJECT)           {changes[li][col]=m_mat[li-1][col];}
                    else if(m_mat[li+1][col] != NO_OBJECT)      {changes[li][col]=m_mat[li+1][col];}
                    else if(m_mat[li][col+1] != NO_OBJECT)      {changes[li][col]=m_mat[li][col+1];}
                    else if(m_mat[li][col-1] != NO_OBJECT)      {changes[li][col]=m_mat[li][col-1];}
                    done=false;
                }
            }
        }
        for (li=1;li<m_size.nbrlign-1;li++)
        {
            for (col=1;col<m_size.nbrcolumn-1;col++)
            {
                if(changes[li][col]!=NO_OBJECT)
                {
                    m_mat[li][col]=changes[li][col];
                }
            }
        }
    }

    for (li=1;li<m_size.nbrlign-1;li++)
    {
        for (col=1;col<m_size.nbrcolumn-1;col++)
        {
            if(changes[li][col]!=NO_OBJECT)
            {
                m_objs[changes[li][col]]->addPoint(li,col,src);
            }
        }
    }
}

//! \brief Assembles the skeleton masks from each object's skeleton
//!
//! \return A finished skeleton mask
bool** ObjectManager::getSkeletonMasks()
{
    bool** objMask;
    Area objArea;
    Pos objOffset;
    bool** mask = createMatrix(m_size, false);
    int li, col;

    for(std::vector<Object*>::iterator it = m_objs.begin(); it!=m_objs.end(); it++)
    {
        if((*it)->getSkeleton() != NULL)
        {
            objArea = (*it)->getArea();
            objMask = (*it)->getSkeleton()->getSkMask();
            objOffset = (*it)->getOffset();
            for (li=0;li<objArea.h;li++)
            {
                for (col=0;col<objArea.w;col++)
                {
                    mask[objOffset.lign + li][objOffset.column + col] |= objMask[li+2][col+2];
                }
            }
        }
    }

    return mask;
}

//! \brief Frees all internal memory and deletes the objects
void ObjectManager::clearObjects()
{
    m_objs.clear();
    freeMatrix(m_mat, m_size);
}

//! \brief Destructs the object manager
ObjectManager::~ObjectManager()
{
    clearObjects();
}

